/*
 * Copyright (c) 2001, 2002 Boris Pruessmann and others. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Common Public License v0.5 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation.
 */
package net.sourceforge.perforce.core.resources;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.PerforceSynchronizer;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.internal.Policy;
import net.sourceforge.perforce.core.internal.utils.FileNameMatcher;
import net.sourceforge.perforce.core.syncinfo.ResourceSyncInfo;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.Status;
import org.eclipse.team.core.IIgnoreInfo;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.Team;

/**
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public abstract class LocalResource extends PlatformObject {

  private static final boolean TRACE =
      getDebugOptionAsBoolean(PerforceCorePlugin.ID + "/localresource/trace");

  private static boolean getDebugOptionAsBoolean(String option) {
    return "true".equalsIgnoreCase(Platform.getDebugOption(option));
  }

  /** Stores the Perforce server. */
  private final IPerforceServer server;
  /** Handle to the underlying resource. */
  private final IResource resource;

  static void trace(String fmt, Object... params) {
    if (TRACE) {
      System.out.print("LocalResource ");
      System.out.printf(fmt, params);
      System.out.println();
    }
  }

  /**
   * Creates either LocalFile or LocalFolder based on the given resource's type.
   * 
   * @param resource any resource.
   * @return the corresponding LocalResource object.
   */
  public static LocalResource createFromResource(IResource resource) {
    LocalResource localResource = null;
    try {
      IPerforceServer server = PerforceCorePlugin.getServerManager().getPerforceServer(resource);
      if (server != null) {
        if (resource instanceof IFile) {
          localResource = LocalFile.create(server, resource);
        } else if (resource instanceof IContainer) {
          localResource = LocalFolder.create(server, resource);
        }
      }
    } catch (PerforceException e) {
      PerforceCorePlugin.log(e.getStatus());
    }

    return localResource;
  }

  //----------------------------------------------------------------------------
  // ---- Constructors

  /**
   * Constructor for LocalResource.
   * 
   * @param server the perforce server.
   * @param resource the local resource.
   * 
   * @pre server != null
   * @pre resource != null
   */
  LocalResource(IPerforceServer server, IResource resource) {
    Policy.assertNotNull(server);
    Policy.assertNotNull(resource);

    this.server = server;
    this.resource = resource;
  }

  //----------------------------------------------------------------------------
  // -- Public Methods

  /**
   * Checks whether the local resource exists.
   * 
   * @return true if resource exists locally.
   */
  public boolean exists() {
    return toResource().exists();
  }

  /** @return false */
  public boolean isIgnored() {
    IResource resource = toResource();
    if (resource == null) {
      return true;
    }

    if (resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT) {
      return false;
    }

    if (hasRemote()) {
      return false;
    }

    // If the resource is a derived resource, it is ignored
    if (resource.isDerived()) {
      return true;
    }

    IContainer parent = resource.getParent();
    if (parent != null) {
      LocalFolder localParent = (LocalFolder) LocalResource.createFromResource(parent);
      if (localParent != null) {
        if (localParent.isIgnored()) {
          return true;
        }
      } else {
        IStatus status =
            new Status(IStatus.WARNING, PerforceCorePlugin.ID, 0,
                "Unable to create LocalResource for '" + parent.getFullPath().toString() + "'",
                null);
        PerforceCorePlugin.log(status);
      }
    }

    FileNameMatcher matcher = new FileNameMatcher();
    String[] p4IgnorePatterns;
    try {
      p4IgnorePatterns = PerforceSynchronizer.getInstance().getIgnored(resource.getParent());
    } catch (PerforceException e) {
      p4IgnorePatterns = new String[0];
    }

    IIgnoreInfo[] ignorePatterns = Team.getAllIgnores();
    for (int i = 0; i < ignorePatterns.length; i++) {
      IIgnoreInfo info = ignorePatterns[i];
      if (info.getEnabled()) {
        matcher.register(info.getPattern(), "true"); //$NON-NLS-1$
      }
    }

    for (int i = 0; i < p4IgnorePatterns.length; i++) {
      matcher.register(p4IgnorePatterns[i], "true"); //$NON-NLS-1$
    }

    // check with all the registered patterns
    return matcher.match(resource.getName());
  }

  /**
   * Returns the absolute pathname string of this abstract pathname.
   * 
   * @return the absolute location.
   */
  public String getAbsolutePath() {
    return getLocation().toOSString();
  }

  /**
   * Returns the associated IResource.
   * 
   * @return the eclipse resource handle.
   */
  public final IResource toResource() {
    return resource;
  }

  /**
   * Returns the resource's sync info.
   * 
   * @return the sync info.
   */
  public abstract ResourceSyncInfo getSyncInfo();

  /**
   * Checks whether there is a remote object for this resource.
   * 
   * @return true if remote exists
   */
  public abstract boolean hasRemote();

  /**
   * Checks whether the resource is locally opened for edit.
   * 
   * @return true if resource is opened for edit.
   */
  public abstract boolean isCheckedOut();

  /**
   * Checks whether the resource is marked as resolved (or nor marked at all).
   * 
   * @return true if resource is marked as resolved.
   */
  public abstract boolean isResolved();

  /**
   * Checks whether the resource is dirty.
   * 
   * @return true if resource is dirty.
   */
  public abstract boolean isDirty();

  /**
   * Checks whether the local version is out of date.
   * 
   * @return true if local version is out of date.
   */
  public abstract boolean isOutOfDate();

  /**
   * Call this method to retrieve the perforce server.
   * 
   * @return the perforce server.
   */
  public final IPerforceServer getServer() {
    return server;
  }

  //----------------------------------------------------------------------------
  // --------- Methods

  /**
   * Call this method to retrieve the location.
   * 
   * @return the location.
   */
  public IPath getLocation() {
    return resource.getLocation();
  }

  public abstract boolean isCheckedOutForAdd();

  public abstract boolean isCheckedOutForDelete();

  public static boolean resourceIsIgnored(IResource resource) {
    LocalResource local = LocalResource.createFromResource(resource);
    return local.isIgnored();
  }

  public static boolean resourceNeedsAdding(IResource resource) {
    LocalResource local = LocalResource.createFromResource(resource);
    if (local == null) return false;
    if (local.hasRemote()) return false;
    if (local.isCheckedOutForAdd()) return false;
    if (local.isIgnored()) return false;
    return true;
  }

  public static boolean oneOrMoreResourcesNeedsAdding(IResource[] resources)
      throws PerforceException {
    boolean enabled = false;
    for (int i = 0; i < resources.length; i++) {
      RepositoryProvider tempProvider = RepositoryProvider.getProvider(resources[i].getProject());
      if (tempProvider == null || !tempProvider.getID().equals(PerforceCorePlugin.getTypeId())) {
        continue;
      }

      if (resources[i] instanceof IContainer) {
        final boolean[] scanResults = new boolean[] {false};
        try {
          ((IContainer) resources[i]).accept(new IResourceVisitor() {
            public boolean visit(IResource resource) {
              // bail-out if we found a resource that needs to be added
              if (scanResults[0]) return false;

              if (resource.getType() == IResource.FOLDER) return !resourceIsIgnored(resource);

              if (LocalResource.resourceNeedsAdding(resource)) {
                scanResults[0] = true;
                return false;
              }
              return true;
            }
          });
          if (scanResults[0]) return true;
        } catch (CoreException e) {
          throw new PerforceException(e.getStatus());
        }
      } else {
        if (LocalResource.resourceNeedsAdding(resources[i])) return true;
      }
    }
    return false;
  }

}
